import ldap


def ldap_auth(server='ldap', port=389,
            base_dn='ou=users,dc=domain,dc=com',
            mode='uid', secure=False):
    """
    to use ldap login with MS Active Directory::

        from gluon.contrib.login_methods.ldap_auth import ldap_auth
        auth.settings.login_methods.append(ldap_auth(
            mode='ad', server='my.domain.controller',
            base_dn='ou=Users,dc=domain,dc=com'))

    to use ldap login with Notes Domino::

        auth.settings.login_methods.append(ldap_auth(
            mode='domino',server='my.domino.server'))

    to use ldap login with OpenLDAP::

        auth.settings.login_methods.append(ldap_auth(
            server='my.ldap.server', base_dn='ou=Users,dc=domain,dc=com'))

    or (if using CN)::

        auth.settings.login_methods.append(ldap_auth(
            mode='cn', server='my.ldap.server',
            base_dn='ou=Users,dc=domain,dc=com'))

    If using secure ldaps:// pass secure=True
    """

    def ldap_auth_aux(username,
            password,
            ldap_server=server,
            ldap_port=port,
            ldap_basedn=base_dn,
            ldap_mode=mode,
            secure=secure):
        try:
            if secure:
                con = ldap.initialize(
                    "ldaps://" + ldap_server + ":" + str(ldap_port))
            else:
                con = ldap.initialize(
                    "ldap://" + ldap_server + ":" + str(ldap_port))
            if ldap_mode == 'ad':
                # Microsoft Active Directory
                con.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
                # credentials should be in the form of username@domain.tld
                con.simple_bind_s(username, password)
                if "@" in username:
                    username = username.split("@")[0]
                # this will throw an index error if the account is not found
                # in the ldap_basedn
                result = con.search_ext_s(
                    ldap_basedn, ldap.SCOPE_SUBTREE,
                    "sAMAccountName=%s" % username, ["sAMAccountName"])[0][1]
            if ldap_mode == 'domino':
                # Notes Domino
                if "@" in username:
                    username = username.split("@")[0]
                con.simple_bind_s(username, password)
            if ldap_mode == 'cn':
                # OpenLDAP (CN)
                dn = "cn="+username+","+ldap_basedn
                con.simple_bind_s(dn, password)
            if ldap_mode == 'uid':
                # OpenLDAP (UID)
                dn = "uid="+username+","+ldap_basedn
                con.simple_bind_s(dn, password)
            con.unbind()
            return True
        except ldap.LDAPError, e:
            return False
        except IndexError, ex: # for AD membership test
            return False
    return ldap_auth_aux
